/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package juggernaut;

import java.io.BufferedReader;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.math.BigInteger;
import java.net.ServerSocket;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.planx.xmlstore.routing.Identifier;
import org.planx.xmlstore.routing.Kademlia;

/**
 *
 * @author shiva
 */
public class DHTServer {

    static void initializeServer(String port, Kademlia[] kademlia) {
        int serverport = 8000;
        if (port != null) {
            serverport = Integer.parseInt(port);
        }
        try {

            ServerSocket srv = new ServerSocket(serverport);
            int max_no_of_connections = 100;

            // Wait for connection from client.

            while (max_no_of_connections > 0) {
                Socket socket = srv.accept();
                System.out.println("Connected to client ");
                DHTWorkerThread s_t = new DHTWorkerThread(socket, kademlia);
                Thread t = new Thread(s_t);
                t.start();
                max_no_of_connections--;
            }
        } catch (IOException e) {

            System.out.println("Exeception caught IOException : " + e);
        }
    }
}

class DHTWorkerThread implements Runnable {

    private Socket server;
    private final Kademlia[] kademlia;

    DHTWorkerThread(Socket socket, Kademlia[] kademlia) {
        this.server = socket;
        this.kademlia = kademlia;
    }

    public void insert_data(String key, String value, DataOutputStream outToClient) {
        try {
            System.out.println("got the key " + key);
            System.out.println("got the value " + value);
            int item = (int) (System.currentTimeMillis() % kademlia.length);
            MessageDigest md5;
            try {
                md5 = MessageDigest.getInstance("MD5");
                md5.update(key.getBytes());
                byte[] md5Bytes = md5.digest();
                md5.reset();
                BigInteger bi = new BigInteger(1, md5Bytes);
                Identifier id = new Identifier(bi);
                kademlia[item].put(id, value);
            } catch (NoSuchAlgorithmException ex) {
                Logger.getLogger(DHTWorkerThread.class.getName()).log(Level.SEVERE, null, ex);
            }


        } catch (IOException ioe) {
            System.out.println("IOException on socket listen: " + ioe);
            ioe.printStackTrace();
        }



    }

    public void retrieve_data(String key, DataOutputStream outToClient) {
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(key.getBytes());
            byte[] md5Bytes = md5.digest();
            md5.reset();
            BigInteger bi = new BigInteger(1, md5Bytes);
            Identifier id = new Identifier(bi);
            int item = (int) (System.currentTimeMillis() % kademlia.length);
            String inputData = (String) kademlia[item].get(id);
            if (inputData == null) {
                inputData = "-1";
            }

            int len = inputData.length();

            System.out.println("sending the length information to client " + len);
            outToClient.writeBytes("" + len + "\n");

            System.out.println("sending the data to client " + inputData);

            outToClient.writeBytes(inputData + "\n");

            System.out.println("Data sent to the client");
        } catch (NoSuchAlgorithmException ex) {
            Logger.getLogger(DHTWorkerThread.class.getName()).log(Level.SEVERE, null, ex);
        } catch (IOException ioe) {
            System.out.println("IOException on socket listen: " + ioe);
            ioe.printStackTrace();
        }


    }

    public void run() {
        BufferedReader rd = null;
        try {
            rd = new BufferedReader(new InputStreamReader(server.getInputStream()));
            DataOutputStream outToClient = new DataOutputStream(server.getOutputStream());
            String buf;
            String buffer = "";
            String[] parts;
            while ((buf = rd.readLine()) != null) {
                buffer += buf.trim();
                while ((parts = buffer.split("\\$")).length >= 3) {
                    System.out.println(parts[0]);
                    System.out.println(parts[1]);
                    System.out.println(parts[2]);
                    String mode = parts[0];
                    String key = parts[1];
                    String value = parts[2];
                    int mod = Integer.parseInt(mode);
                    switch (mod) {
                        case 1: {
                            insert_data(key, value, outToClient);
                            break;
                        }
                        case 2: {
                            retrieve_data(key, outToClient);
                            break;
                        }
                        default: {
                            System.out.println("Invalid operation requested"+buffer+":"+mode);
                        }
                    }

                    buffer = buffer.substring(parts[0].length() + parts[1].length() + parts[2].length() + 2);
                }
            }
        } catch (IOException ex) {
            Logger.getLogger(DHTWorkerThread.class.getName()).log(Level.SEVERE, null, ex);
        } finally {
            try {
                rd.close();
            } catch (IOException ex) {
                Logger.getLogger(DHTWorkerThread.class.getName()).log(Level.SEVERE, null, ex);
            }
        }
    }
}
